12. Pandas-数据框拼接

本章概要

  • 学习材料:两个期间表及价格/成交量键表。
  • 本章任务:运行 lst-concat-periodslst-pandas-merge-join,分别做纵向拼接与键连接。
  • 完成后你将得到:拼接来源列、连接指示列、匹配/未匹配行数。
  • 自我检查:核对主键唯一与行数守恒;多对多放大或键类型不一致时先检查原因。
  • 拓展练习:把同一连接检查拓展应用到沪深两市场清单。

为什么需要数据拼接?

在实际金融分析中,数据往往分散在多个来源:

  • 不同时期的数据分开存储(如月度、季度)
  • 不同市场的数据分别管理(如沪市、深市)
  • 不同指标的数据需要整合(如价格、成交量)

Pandas 提供了三大拼接工具:concatmergejoin

三大拼接工具一览

函数 类比 适用场景
pd.concat() 物理拼接(上下/左右) 结构相同的数据堆叠
pd.merge() SQL JOIN 基于共同列合并不同表
.join() merge 的简化版 基于索引快速合并

运行前预测|平台练习代码

  • 输入预测:运行前先写出 df_pricedf1df2tencent_vol 的业务含义、数据类型或取值范围,并判断哪一个输入最可能改变结果。
  • 结果预测:不展开答案,先预测将得到stock_data 的结果;同时写出方向、数量级或表格/图形结构。
  • 完成要求:能独立说明本任务从输入到“平台练习代码”结果的关键步骤,原样录入平台代码并得到可核对的运行结果。

⭐ 平台练习代码

展开完整代码(投影默认折叠)
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import pandas as pd  # 导入Pandas数据分析库
# 从Excel文件读取数据存入df_price
df_price=pd.read_excel('https://huoran.oss-cn-shenzhen.aliyuncs.com/20230306/xlsx/1632680830600503296.xlsx',index_col=0,sheet_name='Sheet1')
#df1 包含前 100 行,df2 包含剩余的行
df1=df_price.iloc[:100,:] 
df2=df_price.iloc[100:,:]  # 按位置索引提取从Excel文件读取数据存入df_price
# 从Excel文件读取数据存入tencent_vol
tencent_vol=pd.read_excel('https://huoran.oss-cn-shenzhen.aliyuncs.com/20230306/xlsx/1632680830600503296.xlsx',index_col=0,sheet_name='Sheet2')

df_concat=pd.concat([df1,df2],axis=0)  # 拼接数据框并存入df_concat
print(f'2022年有{df_concat.shape[0]}条交易数据')  # 输出2022年有

# 合并数据框并存入df_merge
df_merge=pd.merge(left=df_price['腾讯控股'],right=tencent_vol,right_index=True,left_index=True)
stock_data=df_merge.loc['2022-06-01',:]  # 按标签索引提取数据
print(f"这个交易日的收盘价为{stock_data[0]}元,成交量为{stock_data[2]/10000}万股")  # 输出这个交易日的收盘价为

任务复盘|平台练习代码

运行后核对:核对 df_pricedf1df2tencent_vol 是否按预测参与运算,实际输出是否与预测一致;若不一致,先检查类型、单位、索引/字段和运算顺序。

拓展练习:把输入表替换为本地中国上市公司数据的同结构子集;指出必须保持的字段、数据类型和质量检查。

concat — 轴向拼接的核心函数

pd.concat() 用于沿指定轴将多个数据框拼接在一起

关键参数:

  • objs:要拼接的数据框列表
  • axis:0 = 垂直拼接(行),1 = 水平拼接(列)
  • join'inner'(交集)或 'outer'(并集,默认)
  • ignore_index:是否忽略原索引重新编号
  • keys:创建层次化索引标识来源

concat 垂直拼接示意图

concat 垂直拼接示意图 两个数据框 df1 和 df2 通过 pd.concat 沿 axis=0 方向上下堆叠 df1 前100行 shape: (100, N) df2 剩余行 shape: (M, N) concat axis=0 df1 部分 df2 部分 df_concat

concat 拼接不同时期数据

Listing 1: 拼接不同时期的数据
展开完整代码
import pandas as pd  # 显式导入 Pandas,避免依赖未执行的平台代码块状态

# =============================================================================
# 题目:concat拼接不同月份的股价数据
# =============================================================================
# 在金融时间序列分析中,经常需要将不同时期的数据(如月度数据、季度数据)
# 拼接成更长时间序列以便进行趋势分析。本示例演示如何使用concat()将1月
# 和2月的股价数据垂直拼接成Q1(第一季度)完整数据。

# ==================== 创建1月数据 ====================
# 使用字典创建DataFrame,包含价格和成交量两列
jan_data = pd.DataFrame({
    'price': [10, 11, 12],                    # 股价数据,表示3个交易日的收盘价
    'volume': [1000, 1100, 1200]              # 成交量数据,单位可能是手或股
}, index=pd.date_range('2024-01-01', periods=3))  # 创建日期范围索引,从2024-01-01开始的3天

# ==================== 创建2月数据 ====================
# 创建2月的交易数据,结构相同
feb_data = pd.DataFrame({
    'price': [13, 14, 15],                    # 2月的股价数据,显示上升趋势
    'volume': [1300, 1400, 1500]              # 2月的成交量,呈现增长态势
}, index=pd.date_range('2024-02-01', periods=3))  # 2月日期索引

# ==================== 垂直拼接两月数据 ====================
# pd.concat()默认axis=0,将feb_data追加到jan_data下方
# 拼接后形成包含1月和2月数据的Q1时间序列
q1_data = pd.concat([jan_data, feb_data])  # 垂直拼接,自动对齐列名
assert q1_data.shape == (6, 2), '垂直拼接应得到6行2列'  # 核验拼接后的表结构
assert q1_data.columns.tolist() == ['price', 'volume'], '拼接不应改变列集合'  # 核验列名与顺序
assert q1_data.index.is_unique, '日期索引不应重复'  # 防止时间序列拼接后出现重复键
print("Q1数据:")  # 打印标题
print(q1_data)    # 输出完整的Q1数据,包含6个交易日
Q1数据:
            price  volume
2024-01-01     10    1000
2024-01-02     11    1100
2024-01-03     12    1200
2024-02-01     13    1300
2024-02-02     14    1400
2024-02-03     15    1500

merge — 数据库风格连接

pd.merge() 类似 SQL 的 JOIN 操作,基于共同列或索引合并数据

关键参数:

  • leftright:要合并的两个数据框
  • how:连接方式('inner''outer''left''right'
  • on:用于连接的列名
  • left_index / right_index:是否使用索引作为连接键

merge 四种连接方式

merge 四种连接方式对比 展示 inner、outer、left、right 四种 JOIN 方式的集合关系 交集 inner 只保留匹配行 并集 outer 保留所有行 左全 left 保留左表所有行 右全 right 保留右表所有行 = 左表 (left) = 右表 (right)

merge 合并价格与成交量数据

Listing 2: 使用merge合并DataFrame
展开完整代码
import pandas as pd

# =============================================================================
# 题目:merge函数基于索引合并价格与成交量数据
# =============================================================================
# 在金融分析中,经常需要将不同数据表的信息整合在一起,例如将股价数据与
# 成交量数据合并,以便进行量价分析。本示例演示如何使用pd.merge()函数
# 基于日期索引将中性示例的收盘价和成交量数据进行内连接合并。

# ==================== 构造中性机制演示示例数据 ====================
# 以下固定值只用于演示merge机制,不是任何公司的真实市场依据
dates = pd.to_datetime(['2022-05-30', '2022-05-31', '2022-06-01', '2022-06-02', '2022-06-03'])
df_price = pd.DataFrame(
    {'示例收盘价': [10.20, 10.40, 10.30, 10.60, 10.80]},
    index=dates
)
df_volume = pd.DataFrame(
    {'示例成交量': [1200, 1350, 1280, 1420, 1510]},
    index=dates
)

# 合并前诊断:重复索引会使多对多合并产生行数膨胀
duplicate_keys = int(df_price.index.duplicated().sum() + df_volume.index.duplicated().sum())
print(f'合并键重复数: {duplicate_keys}')
合并键重复数: 0

merge 连接与检查

Listing 3
展开连接与检查代码
# ==================== 基于索引进行内连接合并 ====================
# pd.merge()实现类似SQL的JOIN操作:
# left:左表,取df_price中的'示例收盘价'列
# right:右表,取df_volume(成交量数据)
# left_index=True/right_index=True:使用左右两表的索引(日期)作为连接键
# how='inner':内连接,只保留两个表中索引都存在的日期
df_merge = pd.merge(
    left=df_price['示例收盘价'],  # 左表:中性价格演示序列
    right=df_volume,             # 右表:中性成交量演示序列
    left_index=True,             # 布尔值,指定使用左表的索引作为连接键
    right_index=True,            # 布尔值,指定使用右表的索引作为连接键
    how='inner'                  # 连接方式,'inner'表示只保留匹配的行
)

# ==================== 输出合并后的数据 ====================
print("合并后的数据:")  # 打印标题
print(df_merge.head())  # 显示前5行数据,包含收盘价和成交量
expected_columns = ['示例收盘价', '示例成交量']
assert duplicate_keys == 0, '请先处理重复合并键'
assert df_merge.shape == (5, 2), '预期得到5行2列'
assert df_merge.index.equals(pd.DatetimeIndex(dates)), '预期保留完整日期索引'
assert df_merge.columns.tolist() == expected_columns, '预期列名与输入字段一致'
print(f'检查: shape={df_merge.shape}, index=日期索引, columns={expected_columns}')
合并后的数据:
            示例收盘价  示例成交量
2022-05-30   10.2   1200
2022-05-31   10.4   1350
2022-06-01   10.3   1280
2022-06-02   10.6   1420
2022-06-03   10.8   1510
检查: shape=(5, 2), index=日期索引, columns=['示例收盘价', '示例成交量']

merge 日期键查询

Listing 4
展开日期查询代码
# ==================== 查询特定日期的交易数据 ====================
# 使用.loc通过日期索引定位特定行的数据
stock_data = df_merge.loc['2022-06-01', :]  # 获取2022-06-01这一行的所有列数据
print(f"\n2022-06-01的交易数据:")  # 打印日期标题
print(f"  机制演示收盘价: {stock_data['示例收盘价']:.2f}")
print(f"  机制演示成交量: {stock_data['示例成交量']}")

2022-06-01的交易数据:
  机制演示收盘价: 10.30
  机制演示成交量: 1280.0

join — 基于索引的简化合并

.join()merge() 的便捷方法,专门用于基于索引合并

与 merge 的区别:

  • 语法更简洁:df1.join(df2) 即可
  • 默认使用索引作为连接键
  • 默认执行左连接(how='left'

join 合并收盘价与成交量

Listing 5: 使用join进行索引合并
展开完整代码
import pandas as pd

# =============================================================================
# 题目:join函数简化索引合并操作
# =============================================================================
# join()是merge()的便捷方法,专门用于基于索引合并数据框,语法更加简洁。
# 本示例演示如何使用join()方法将中性收盘价与成交量数据合并,
# 相比merge(),join()更适合处理基于索引的合并场景。

# ==================== 构造中性机制演示示例数据 ====================
# 固定值只用于演示join机制,不是真实公司行情
dates = pd.to_datetime(['2022-05-30', '2022-05-31', '2022-06-01', '2022-06-02', '2022-06-03'])
df_price = pd.DataFrame(
    {'示例收盘价': [10.20, 10.40, 10.30, 10.60, 10.80]},
    index=dates
)
df_volume = pd.DataFrame(
    {'示例成交量': [1200, 1350, 1280, 1420, 1510]},
    index=dates
)

# ==================== 使用join进行索引合并 ====================
# df_price[['示例收盘价']]:从价格数据框中提取中性示例列
# .join(df_volume):将成交量数据框基于索引合并到收盘价数据框
# how='inner':内连接,只保留两个表中索引都存在的日期
df_join = df_price[['示例收盘价']].join(  # 调用左表的join方法
    df_volume,                          # 要合并的右表(中性成交量数据)
    how='inner'                         # 连接方式,内连接保留匹配的索引
)

# ==================== 输出合并结果 ====================
print("使用join合并:")  # 打印提示信息
print(df_join.head())  # 显示前5行数据,包含收盘价和成交量
join_duplicate_keys = int(df_price.index.duplicated().sum() + df_volume.index.duplicated().sum())  # 统计两张表的重复连接键
assert join_duplicate_keys == 0, '请先处理重复连接键'  # 防止重复键造成行数膨胀
assert df_join.shape == (5, 2), 'join应得到5行2列'  # 核验内连接后的表结构
assert df_join.index.equals(pd.DatetimeIndex(dates)), 'join应保留完整日期交集'  # 核验连接键交集
assert df_join.columns.tolist() == ['示例收盘价', '示例成交量'], 'join列名与输入不一致'  # 核验输出列集合
assert df_join.equals(df_price[['示例收盘价']].merge(df_volume, left_index=True, right_index=True, how='inner')), 'join与merge结果应等价'  # 核验相同连接语义
print(f'检查: shape={df_join.shape}, columns={df_join.columns.tolist()}')
使用join合并:
            示例收盘价  示例成交量
2022-05-30   10.2   1200
2022-05-31   10.4   1350
2022-06-01   10.3   1280
2022-06-02   10.6   1420
2022-06-03   10.8   1510
检查: shape=(5, 2), columns=['示例收盘价', '示例成交量']

应用场景:多市场数据整合

Listing 6: 合并不同市场的数据
展开完整代码
import pandas as pd

# =============================================================================
# 题目:concat拼接沪市和深市的股票数据
# =============================================================================
# 在A股市场中,股票分为上海证券交易所(沪市)和深圳证券交易所(深市)。
# 实际分析中常需要将两个市场的数据整合在一起进行跨市场比较分析。
# 本示例演示如何使用concat()的keys参数为拼接后的数据添加层次化索引,
# 标识每条数据来源于哪个市场。

# ==================== 创建沪市股票数据 ====================
# 构造仅用于拼接机制的沪市中性示例表,不代表真实市场依据
sh_data = pd.DataFrame({
    'sample_id': ['SH-SAMPLE-01', 'SH-SAMPLE-02'],  # 使用中性标识避免暗示具名公司报价
    'example_value': [10.5, 45.2]                    # 固定数值只用于演示拼接结构
})

# ==================== 创建深市股票数据 ====================
# 构造深市中性示例表,结构与沪市示例相同
sz_data = pd.DataFrame({
    'sample_id': ['SZ-SAMPLE-01', 'SZ-SAMPLE-02'],  # 使用中性标识表示深市来源
    'example_value': [12.3, 8.5]                     # 固定数值不用于任何市场判断
})

# ==================== 垂直拼接并添加市场标识 ====================
# pd.concat()的keys参数为拼接后的数据创建层次化索引
# keys=['沪市', '深市']:为两个数据框分别添加标签
# names=['市场']:指定层次化索引的级别名称
all_stocks = pd.concat([sh_data, sz_data], keys=['沪市', '深市'], names=['市场'])
assert all_stocks.shape == (4, 2), '跨市场拼接应得到4行2列'  # 核验拼接后的表结构
assert all_stocks.index.names == ['市场', None], '应保留市场层级索引'  # 核验keys生成的来源索引
assert all_stocks.columns.tolist() == ['sample_id', 'example_value'], '拼接后列集合不应改变'  # 核验两市表结构一致
assert all_stocks['sample_id'].is_unique, '中性标识不应重复'  # 防止跨市场样例键冲突
print("两市股票:")  # 打印标题
print(all_stocks)    # 输出包含市场标识的合并数据,可通过层次化索引区分市场
两市股票:
         sample_id  example_value
市场                               
沪市 0  SH-SAMPLE-01           10.5
   1  SH-SAMPLE-02           45.2
深市 0  SZ-SAMPLE-01           12.3
   1  SZ-SAMPLE-02            8.5

三大工具选择指南

场景 推荐函数 原因
结构相同的数据上下/左右拼接 pd.concat() 简单高效
基于共同列合并不同表 pd.merge() 功能最全,类似 SQL
基于索引快速合并 .join() 语法最简洁
需要层次化索引标识来源 pd.concat(keys=...) 支持 keys 参数

本章小结

核心知识点回顾:

  • pd.concat():轴向拼接,axis=0 垂直,axis=1 水平
  • pd.merge():数据库风格连接,支持四种 JOIN 方式
  • .join():基于索引的便捷合并方法
  • 拼接前应确认列名一致性索引对齐
  • 金融分析中常用于整合多时期、多市场数据

随堂练习

  • 问题 1|需要准备哪些数据?:两个期间表及价格/成交量键表。
  • 问题 2|需要完成哪些操作?:运行 lst-concat-periodslst-pandas-merge-join,分别做纵向拼接与键连接。
  • 问题 3|应得到哪些结果?:拼接来源列、连接指示列、匹配/未匹配行数。
  • 问题 4|怎样确认结果可靠?:核对主键唯一与行数守恒;多对多放大或键类型不一致时先检查原因。
  • 问题 5|换一个情境,怎样继续应用?:把同一连接检查拓展应用到沪深两市场清单。
  • 作答提示:请依次写清所用数据、分析过程、所得结果、核对方法和拓展思考。课程所需数据见前言中的下载入口;教学平台固定题按页面说明完成。

教师参考解答|答案与说明 1

  • 逐项答案:下方 merge(..., validate='one_to_one') 演示数据库风格连接;merge 按键、join 常按索引、concat 沿轴堆叠;拓展应用到长三角公司基础表×财报时改用 one_to_many,以未匹配数、行数和金额总额核对。

  • 所用数据与字段:左右表连接键 code 唯一

  • 参考代码或推导(平台保护块之外):

教师参考解答|代码 1

展开代码(代码区可独立滚动)
import pandas as pd  # 导入表格库
left=pd.DataFrame({'code':['A','B'],'price':[10,20]})  # 创建左表
right=pd.DataFrame({'code':['A','B'],'volume':[100,200]})  # 创建右表
answer=left.merge(right,on='code',how='inner',validate='one_to_one')  # 一对一合并
print(answer.shape,answer.columns.tolist())  # 核对结构

教师参考解答|答案与说明 2

  • 参考结果:(2,3),列为 code、price、volume
  • 边界 / 局限:连接方式取决于业务全集
  • 常见错误:未设 validate;重复键膨胀